package com.ctakit.kiwi.web.util;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.ctakit.kiwi.common.core.util.MyFileUtils;
import com.ctakit.kiwi.common.core.util.Resources;
import com.ctakit.kiwi.common.core.util.WebConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.util.*;


/**
 * HttpServletRequest帮助类
 *
 * @author andyhome
 */
@Slf4j
public class RequestUtils {
    public static UrlPathHelper urlPathHelper = new UrlPathHelper();
    private static String[] modelRequestAttribute = Resources.getString(WebConstants.Request_MODEL_REQUEST_ATRRIBUTE, "page,error,errors,message").split(",");

    /**
     * 获取QueryString的参数，并使用URLDecoder以UTF-8格式转码。如果请求是以post方法提交的，
     * 那么将通过HttpServletRequest#getParameter获取。
     *
     * @param request web请求
     * @param name    参数名称
     * @return
     */
    public static String getQueryParam(HttpServletRequest request, String name) {
        if (StrUtil.isBlank(name)) {
            return null;
        }

        if (request.getMethod().equalsIgnoreCase(WebConstants.Request_POST)) {
            return request.getParameter(name);
        }
        String s = request.getQueryString();
        if (StrUtil.isBlank(s)) {
            return null;
        }
        try {
            s = URLDecoder.decode(s, WebConstants.SYSTEM_CHARSET);
        } catch (UnsupportedEncodingException e) {
            log.error("encoding " + WebConstants.SYSTEM_CHARSET + " not support?", e);
        }
        String[] values = parseQueryString(s).get(name);
        if (values != null && values.length > 0) {
            return values[values.length - 1];
        } else {
            return null;
        }
    }

    public static Map<String, Object> getQueryParams(HttpServletRequest request) {
        Map<String, String[]> map;
        if (request.getMethod().equalsIgnoreCase(WebConstants.Request_POST)) {
            map = request.getParameterMap();
        } else {
            String s = request.getQueryString();
            if (StrUtil.isBlank(s)) {
                return new HashMap<String, Object>();
            }
            try {
                s = URLDecoder.decode(s, WebConstants.DEFAULT_ENCODING);
            } catch (UnsupportedEncodingException e) {
                log.error("encoding " + WebConstants.DEFAULT_ENCODING + " not support?", e);
            }
            map = parseQueryString(s);
        }

        Map<String, Object> params = new HashMap<String, Object>(map.size());
        int len;
        for (Map.Entry<String, String[]> entry : map.entrySet()) {
            len = entry.getValue().length;
            String key = entry.getKey();
            if (len == 1) {
                params.put(key, entry.getValue()[0]);
            } else if (len > 1) {
                params.put(key, entry.getValue());
            }
        }
        return params;
    }

    /**
     * Parses a query string passed from the client to the server and builds a
     * <code>HashTable</code> object with key-value pairs. The query string
     * should be in the form of a string packaged by the GET or POST method,
     * that is, it should have key-value pairs in the form <i>key=value</i>,
     * with each pair separated from the next by a &amp; character.
     *
     * <p>
     * A key can appear more than once in the query string with different
     * values. However, the key appears only once in the hashtable, with its
     * value being an array of strings containing the multiple values sent by
     * the query string.
     *
     * <p>
     * The keys and values in the hashtable are stored in their decoded form, so
     * any + characters are converted to spaces, and characters sent in
     * hexadecimal notation (like <i>%xx</i>) are converted to ASCII characters.
     *
     * @param s a string containing the query to be parsed
     * @return a <code>HashTable</code> object built from the parsed key-value
     * pairs
     * @throws IllegalArgumentException if the query string is invalid
     */
    public static Map<String, String[]> parseQueryString(String s) {
        String valArray[] = null;
        if (s == null) {
            throw new IllegalArgumentException();
        }
        Map<String, String[]> ht = new HashMap<String, String[]>();
        StringTokenizer st = new StringTokenizer(s, "&");
        while (st.hasMoreTokens()) {
            String pair = (String) st.nextToken();
            int pos = pair.indexOf('=');
            if (pos == -1) {
                continue;
            }
            String key = pair.substring(0, pos);
            String val = pair.substring(pos + 1, pair.length());
            if (ht.containsKey(key)) {
                String oldVals[] = (String[]) ht.get(key);
                valArray = new String[oldVals.length + 1];
                for (int i = 0; i < oldVals.length; i++) {
                    valArray[i] = oldVals[i];
                }
                valArray[oldVals.length] = val;
            } else {
                valArray = new String[1];
                valArray[0] = val;
            }
            ht.put(key, valArray);
        }
        return ht;
    }

    public static Map<String, String> getRequestMap(HttpServletRequest request, String prefix) {
        return getRequestMap(request, prefix, false);
    }

    public static Map<String, String> getRequestMapWithPrefix(HttpServletRequest request, String prefix) {
        return getRequestMap(request, prefix, true);
    }

    /**
     * 请求中参数转化为map
     *
     * @param request
     * @param prefix
     * @param nameWithPrefix
     * @return
     */
    private static Map<String, String> getRequestMap(HttpServletRequest request, String prefix, boolean nameWithPrefix) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration<String> names = request.getParameterNames();
        String name, key, value;
        while (names.hasMoreElements()) {
            name = names.nextElement();
            if (name.startsWith(prefix)) {
                key = nameWithPrefix ? name : name.substring(prefix.length());
                value = StrUtil.join(",", (Object[]) request.getParameterValues(name));
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * request中attribute 对应的值转化为map
     *
     * @param request
     * @param prefix
     * @param nameWithPrefix
     * @return
     */
    public static Map<String, Object> getRequestAttributeMap(HttpServletRequest request, String prefix, boolean nameWithPrefix) {
        Map<String, Object> map = new HashMap<String, Object>();
        Enumeration<String> names = request.getAttributeNames();
        String name, key;
        while (names.hasMoreElements()) {
            name = names.nextElement();
            if (name.startsWith(prefix)) {
                key = nameWithPrefix ? name : name.substring(prefix.length());
                map.put(key, request.getAttribute(name));
            }
        }
        return map;
    }

    /**
     * request中attribute对应的值(只取配置文件中定义的)转化为map
     *
     * @param request
     * @return
     */
    public static Map<String, Object> getRequestAttributeMapForModel(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<String, Object>();

        for (String key : modelRequestAttribute) {
            Object value = request.getAttribute(key);
            if (value != null) {
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * 获取访问者IP
     * <p>
     * 在一般情况下使用Request.getRemoteAddr()即可，但是经过nginx等反向代理软件后，这个方法会失效。
     * <p>
     * 本方法先从Header中获取X-Real-IP，如果不存在再从X-Forwarded-For获得第一个IP(用,分割)，
     * 如果还不存在则调用Request .getRemoteAddr()。
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (!StrUtil.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        ip = request.getHeader("X-Forwarded-For");
        if (!StrUtil.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值，第一个为真实IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            return request.getRemoteAddr();
        }
    }

    /**
     * 获得当的访问路径
     * <p>
     * HttpServletRequest.getRequestURL+"?"+HttpServletRequest.getQueryString
     *
     * @param request
     * @return
     */
    public static String getLocation(HttpServletRequest request) {
        StringBuffer buff = request.getRequestURL();
        String uri = request.getRequestURI();
        String origUri = urlPathHelper.getOriginatingRequestUri(request);
        buff.replace(buff.length() - uri.length(), buff.length(), origUri);
        String queryString = urlPathHelper.getOriginatingQueryString(request);
        if (queryString != null) {
            buff.append("?").append(queryString);
        }
        return buff.toString();
    }


    @SuppressWarnings("unchecked")
    public static void addError(String name, String message) {
        HttpServletRequest request = ThreadLocalUtils.getRequest();
        Map<String, String> errors = (Map<String, String>) request.getAttribute(WebConstants.Request_ERRORS);
        if (errors == null) {
            errors = new LinkedHashMap<String, String>();
            request.setAttribute(WebConstants.Request_ERRORS, errors);
        }
        errors.put(name, message);

    }

    /**
     * 获得访问路径
     *
     * @param request
     */
    public static String getLookupPath(HttpServletRequest request) throws IllegalStateException {

        String uri = urlPathHelper.getLookupPathForRequest(request);

        return uri;
    }

    /**
     * 获得访问路径，去除访问路径的后缀
     *
     * @param request
     */
    public static String getLookupPathNoExtension(HttpServletRequest request) throws IllegalStateException {
        String uri = urlPathHelper.getLookupPathForRequest(request);
        // 处理后缀，去掉后缀. /user/list.do ===> /user/list
        return MyFileUtils.getFileNameNoExtension(uri);
    }

    /**
     * 获得访问路径的后缀
     *
     * @param request
     */
    public static String getLookupPathExtension(HttpServletRequest request) throws IllegalStateException {
        String uri = urlPathHelper.getLookupPathForRequest(request);
        return MyFileUtils.getFileNameExtension(uri);
    }

    /**
     * 请求参数值为整型
     *
     * @param request
     * @param paranm
     * @return
     */
    public static Integer getInteger(HttpServletRequest request, String paranm) {
        String i = request.getParameter(paranm);
        if (NumberUtil.isInteger(i)) {
            return Integer.parseInt(i);
        }
        return null;
    }

    /**
     * 请求参数值为浮点型
     *
     * @param request
     * @param paranm
     * @return
     */
    public static float getFloat(HttpServletRequest request, String paranm) {
        String i = request.getParameter(paranm);
        if (NumberUtil.isNumber(i)) {
            return Float.parseFloat(i);
        }
        return 0f;
    }

    /**
     * 请求参数值为BigDecimal
     *
     * @param request
     * @param paranm
     * @return
     */
    public static BigDecimal getBigDecimal(HttpServletRequest request, String paranm) {
        String i = request.getParameter(paranm);
        if (NumberUtil.isNumber(i)) {
            return new BigDecimal(i);
        }
        return new BigDecimal("0");
    }

    /**
     * 请求参数值为整型
     *
     * @param request
     * @param paranm
     * @return
     */
    public static int getInt(HttpServletRequest request, String paranm) {
        Integer i = getInteger(request, paranm);
        if (i == null) {
            return 0;
        }
        return i;
    }

    /**
     * 请求参数值为长整型
     *
     * @param request
     * @param paranm
     * @return
     */
    public static Long getLong(HttpServletRequest request, String paranm) {
        String i = request.getParameter(paranm);
        if (NumberUtil.isNumber(i)) {
            return Long.parseLong(i);
        }
        return null;
    }

    /**
     * 请求参数值为长整型
     *
     * @param request
     * @param paranm
     * @return
     */
    public static List<Long> getLongValues(HttpServletRequest request, String paranm) {
        String[] v = request.getParameterValues(paranm);
        List<Long> values = new ArrayList<Long>();
        if (v != null) {
            for (int j = 0; j < v.length; j++) {
                if (NumberUtil.isNumber(v[j])) {
                    values.add(Long.parseLong(v[j]));
                }
            }
        }
        return values;
    }

    /**
     * 请求参数值为整型数组
     *
     * @param request
     * @param paranm
     * @return
     */
    public static List<Integer> getIntValues(HttpServletRequest request, String paranm) {
        String[] v = request.getParameterValues(paranm);
        List<Integer> values = new ArrayList<Integer>();
        if (v != null) {
            for (int j = 0; j < v.length; j++) {
                if (NumberUtil.isNumber(v[j])) {
                    values.add(Integer.parseInt(v[j]));
                }
            }
        }
        return values;
    }

    /**
     * 请求参数值为整型
     *
     * @param request
     * @param paranm
     * @return
     */
    public static long getlong(HttpServletRequest request, String paranm) {
        Long i = getLong(request, paranm);
        if (i == null) {
            return 0;
        }
        return i;
    }

    /**
     * 请求参数值
     *
     * @param request
     * @param paranm
     * @return
     */
    public static String getString(HttpServletRequest request, String paranm) {
        return request.getParameter(paranm);
    }

    /**
     * 请求参数值
     *
     * @param request
     * @param paranm
     * @return
     */
    public static List<String> getStringValues(HttpServletRequest request, String paranm) {
        String[] v = request.getParameterValues(paranm);
        List<String> values = new ArrayList<String>();
        if (v != null) {
            for (int j = 0; j < v.length; j++) {
                if (StrUtil.isNotBlank(v[j])) {
                    values.add(v[j]);
                }
            }
        }
        return values;
    }

}
